#include "mpsc.h"
#include "spinlock.h"
#include "console.h"

void init_mpsc_node(mpsc_node_t *node) {
    node->next = 0;
}

void init_mpsc_queue(mpsc_queue_t *queue) {
    initlock(&queue->lock, "mpsc_queue.lock");
    queue->head = 0;
    queue->tail = 0;
    queue->locked = 0;
}

/**
 * returns 1 if the original queue is empty.
 */
int mpsc_push(mpsc_queue_t *queue, mpsc_node_t *node) {
    int is_empty = 0;

    acquire(&queue->lock);

    if (queue->tail)
        queue->tail->next = node;
    else {
        is_empty = MPSC_EMPTY;
        queue->head = node;
    }
    queue->tail = node;

    release(&queue->lock);

    return is_empty;
}

void mpsc_pop_acquire(mpsc_queue_t *queue) {
    acquire(&queue->lock);
    queue->locked = 1;

    // // check for potential contention.
    // if (queue->head == queue->tail) {
    //     acquire(&queue->lock);
    //     queue->locked = 1;
    //     if (queue->head->next) {
    //         // no contention in accessing the head.
    //         release(&queue->lock);
    //         queue->locked = 0;
    //     }
    // }
}

void mpsc_pop_release(mpsc_queue_t *queue) {
    if (queue->locked) {
        queue->locked = 0;
        release(&queue->lock);
    }
}

/**
 * returns the next head.
 */
mpsc_node_t *mpsc_raw_pop(mpsc_queue_t *queue) {
    assert(queue->head);

    mpsc_node_t *node = queue->head->next;
    queue->head = node;

    assert(queue->locked || node);
    if (!node)
        queue->tail = 0;

    return node;
}

/**
 * returns the next head.
 */
mpsc_node_t *mpsc_pop(mpsc_queue_t *queue) {
    mpsc_pop_acquire(queue);
    mpsc_node_t *node = mpsc_raw_pop(queue);
    mpsc_pop_release(queue);
    return node;
}